package net.wangds.common.context;

import net.wangds.common.GeneralParentObject;
import net.wangds.common.factory.Factory;

import org.apache.commons.collections.FastHashMap;

import java.util.ServiceLoader;
import java.util.Set;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * 通过Service-Provider方式实现的BeanContext工厂类.
 * <p>
 * 修改列表:
 * <ol>
 * <li>确定版本,补充注释. by wangds@gmail.com 2013-10-12 22:43</li>
 * <li>
 * <ul>
 * <li>一个工厂类只能产生单例的BeanContext;</li>
 * <li>添加静态工厂方法{@link #getBeanContext()}，使用此方法永远只能 获得同一单例上下文.</li>
 * </ul>
 * by wangds@gmail.com 2013-10-12 23:04</li>
 * </ol>
 * </p>
 * 
 * @author wangds@gmail.com
 * @version 0.1.2
 * @see java.util.ServiceLoader
 */
public class BeanContextFactory extends GeneralParentObject implements Factory<BeanContext> {

  /**
   * .
   */
  private static final BeanContextFactory _FACTORY = new BeanContextFactory();

  /**
   * 从默认工厂对象中获得上下文.
   * <p>
   * 如果想获得整个应用的上下文，应该使用本方法.
   * </p>
   * 
   * @return 默认工厂类的上下文对象.
   */
  public static BeanContext getBeanContext(String schema) {
    return _FACTORY.getInstance(schema);
  }

  /**
   * 保持单例上下文的引用.
   * <p>
   * </p>
   */
  private final FastHashMap instanceMap = new FastHashMap();

  /**
   * {@linkplain BeanContextFactory#instanceMap}操作的锁.
   */
  private final ReentrantReadWriteLock instanceMapLock = new ReentrantReadWriteLock();

  /**
   * 获得{@linkplain BeanContext}接口的实例对象.
   * <p>
   * 通过Service-Provider方式获得接口实例.通过
   * META-INF/services/net.wangds.common.context.BeanContext文件定义。如果存在多份
   * 配置返回第一个配置对应的实例。
   * </p>
   * 
   * @return 上下文.
   */
  @Override
  public BeanContext getInstance(Object... args) {
    BeanContext res = null;
    res = loadNewInstance(args);
    return res;
  }

  /**
   * 完成从配置生成实例的代码.
   * <p>
   * 使用了Service-Provider方式.
   * </p>
   * 
   * @return 上下文对象.
   * @see #getInstance()
   * @see java.util.ServiceLoader
   */
  private BeanContext loadNewInstance(Object... args) {
    BeanContext res = null;

    if (this.instanceMap.size() == 0) {
      loadInstanceMap();
    }

    Lock lock = instanceMapLock.readLock();
    lock.lock();
    try {
      Set<String> schemas = new java.util.HashSet<String>();

      for (Object c : args) {
        if (c == null) {
          continue;
        }
        String schema = null;
        schema = (String) c;
        schemas.add(schema);
      }
      if (schemas.size() == 0) {
        return res;
      }

      for (String s : schemas) {
        BeanContext ctx = (BeanContext) this.instanceMap.get(s);
        if (ctx == null) {
          continue;
        }
        if (res == null) {
          res = ctx;
        } else {
          res.appendContext(ctx);
        }
      }
    } finally {
      lock.unlock();
    }
    return res;
  }

  /**
   * .
   * 
   * @author wangds 2015年12月21日 下午8:33:20.
   */
  private void loadInstanceMap() {
    Lock lock = instanceMapLock.writeLock();
    lock.lock();
    try {
      ServiceLoader<BeanContext> loader = ServiceLoader.load(BeanContext.class);
      // assert loader != null : "无法生成BeanContext接口的ServiceLoader对象.";
      String schema = null;
      for (BeanContext c : loader) {
        schema = c.getSchema();
        this.instanceMap.put(schema, c);
        this.testNeedAnException();
      }
    } finally {
      lock.unlock();
    }
  }
  

  {
    // 开启快速Map功能
    instanceMap.setFast(true);
  }

}
